#ifndef COMMON_SHARED_LIBRARY_H_
#define COMMON_SHARED_LIBRARY_H_
#include <fermat/common/mutex.h>
#include <string>
#include <memory>

namespace fermat {

	/// The SharedLibrary class dynamically 
	/// loads shared libraries at run-time.
class  SharedLibrary {
public:
	enum Flags
	{
		/// The SharedLibrary class dynamically 
		/// loads shared libraries at run-time.
		SHLIB_GLOBAL = 1,
			/// On platforms that use dlopen(), use RTLD_LOCAL instead of RTLD_GLOBAL.
			///
			/// Note that if this flag is specified, RTTI (including dynamic_cast and throw) will
			/// not work for types defined in the shared library with GCC and possibly other
			/// compilers as well. See http://gcc.gnu.org/faq.html#dso for more information.
			///
			/// This flag is ignored on platforms that do not use dlopen().
		SHLIB_LOCAL  = 2  
		
	};
	/// Creates a SharedLibrary object.
	SharedLibrary();
		/// Creates a SharedLibrary object and loads a library
		/// from the given path.		
		
	SharedLibrary(const std::string& path);
		/// Creates a SharedLibrary object and loads a library
		/// from the given path, using the given flags. 
		/// See the Flags enumeration for valid values.

	SharedLibrary(const std::string& path, int flags);
		/// Destroys the SharedLibrary. The actual library
		/// remains loaded.


	virtual ~SharedLibrary();
		/// Loads a shared library from the given path.
		/// Throws a LibraryAlreadyLoadedException if
		/// a library has already been loaded.
		/// Throws a LibraryLoadException if the library
		/// cannot be loaded.
	void load(const std::string& path);
		/// Loads a shared library from the given path,
		/// using the given flags. See the Flags enumeration
		/// for valid values.
		/// Throws a LibraryAlreadyLoadedException if
		/// a library has already been loaded.
		/// Throws a LibraryLoadException if the library
		/// cannot be loaded.

	void load(const std::string& path, int flags);

		/// Unloads a shared library.
	void unload();

	std::string errMsg() { return _errmsg; }
	bool valid() { return _handle != 0; }

		/// Returns true iff a library has been loaded.
	bool isLoaded() const;

		/// Returns true iff the loaded library contains
		/// a symbol with the given name.
	bool hasSymbol(const std::string& name);

		/// Returns the address of the symbol with
		/// the given name. For functions, this
		/// is the entry point of the function.
		/// Throws a NotFoundException if the symbol
		/// does not exist.
	void* getSymbol(const std::string& name);

			/// Returns the path of the library, as
		/// specified in a call to load() or the 
		/// constructor.	
	const std::string& getPath() const;

			/// Returns the platform-specific filename prefix
		/// for shared libraries.
		/// Most platforms would return "lib" as prefix, while
	    /// on Cygwin, the "cyg" prefix will be returned.	
	static std::string prefix();

			/// Returns the platform-specific filename suffix
		/// for shared libraries (including the period).
		/// In debug mode, the suffix also includes a
		/// "d" to specify the debug version of a library.	
	static std::string suffix();

			/// Returns the platform-specific filename 
		/// for shared libraries by prefixing and suffixing name
		/// with prefix() and suffix()	
	static std::string getOSName(const std::string& name);


private:
	SharedLibrary(const SharedLibrary&);
	SharedLibrary& operator = (const SharedLibrary&);
	bool loadImpl(const std::string &path, int flag);
private:
	std::string  _path;
	std::string  _errmsg;
	void*        _handle;
	static Mutex _mutex;
};

typedef std::shared_ptr<SharedLibrary> SharedLibraryPtr;

}

#endif